<?php
namespace Wxpay;

use app\common\model\PayLog;
//微信支付
class Wxpay{
    private $config =[
        "appid"  => "wx1b5c6a97e8b6fbbc",  // 开放平台或商户平台APPID
        "mch_id" => "1558600161",  // 商户平台 商户号
        "key"    => "bfca556b7dae3634d481553b2b709efd",  // 商户平台 秘钥KEY
        "TOKEN"  => "1111111111",  // 此参数非必传 有的前端在jsapi 支付时会要求返回signature 参数  此参数即为此准备
    ];
    public function index($param,$openid="",$sharing='N')
    {
        $order = [
            'out_trade_no'  => $param['out_trade_no'],// 订单号
            'total_fee'     => intval($param['total_fee']),// 订单金额  以(分)为单位
            'body'          => $param['body'],// 商品描述
            'notify_url'    => $param['notify_url'], //回调地址
            'spbill_create_ip' => $param['spbill_create_ip'], //对应IP
            'trade_type'    => $param['trade_type'],  //对应支付类型
            'profit_sharing'=>$sharing
        ];

        #当支付类型为JAPI时  openid  必传
        if($param['trade_type'] == "JSAPI")
        {
            $order['openid'] = $openid;
        }

        #统一下单 获取prepay_id
        $unified_order=$this->unifiedOrder($order);

        if ($unified_order){
            #获取当前时间戳
            $time = time();
            #JSAPI
            if($param['trade_type'] == 'JSAPI')
            {
                #组合jssdk需要用到的数据
                $data = [
                    'appId'     => $this->config['appid'], //appid
                    'timeStamp' => strval($time), //时间戳
                    'nonceStr'  =>$unified_order['nonce_str'],// 随机字符串
                    'package'   => 'prepay_id='.$unified_order['prepay_id'],// 预支付交易会话标识
                    'signType'  => 'MD5',      //加密方式
                ];
                // 生成签名
                $data['paySign']=$this->makeSign($data);
                // #有的可能会有需求signature 此参数的在此加密一下即可
                // $token = $this->config['token'];
                // $tmpArr = array($token, strval($time),$unified_order['nonce_str']);
                // sort($tmpArr, SORT_STRING);
                // $tmpStr = implode( $tmpArr );
                // $tmpStr = sha1($tmpStr);
                // $data['signature'] = $tmpStr;
            }
            #APP
            elseif($param['trade_type'] == 'APP')
            {
                $data  = [
                    'appid'     => $this->config['appid'],
                    'partnerid' => $this->config['mch_id'],
                    'prepayid'  => $unified_order['prepay_id'],
                    'package'   => 'Sign=WXPay',
                    'noncestr'  => $unified_order['nonce_str'],// 随机字符串
                    'timestamp' => strval($time), //时间戳
                ];
                //生成签名
                $data['sign'] =  $this->makeSign($data);
            }
            return $data;
        }

        return false;

    }
    /**
     * 统一下单
     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型：JSAPI，NATIVE，APP)
     */
    public function unifiedOrder($order)
    {

        $config =[
            'appid'     => $this->config['appid'], //appid
            'mch_id'    => $this->config['mch_id'], //商户号ID
            'nonce_str' => $this->getNonceStr()
        ];

        # 合并配置数据和订单数据
        $data=array_merge($order,$config);

        # 生成签名
        $sign=$this->makeSign($data);
        $data['sign']=$sign;
        #转换成xml
        $xml=$this->toXml($data);
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';  //接收xml数据的文件
        $header[] = "Content-type: text/xml";      //定义content-type为xml,注意是数组
        $ch = curl_init ($url);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地没有指定curl.cainfo路径的错误
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $response = curl_exec($ch);
        if(curl_errno($ch)){
            # 显示报错信息；终止继续执行
            die(curl_error($ch));
        }
        curl_close($ch);

        #转换成数组
        $result=$this->toArray($response);

        #显示错误信息
        if ($result['return_code']=='FAIL')
        {
            return false ;
        }
        $result['sign']=$sign;
        $result['nonce_str']=$this->getNonceStr();
        return $result;
    }
    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function makeSign($data)
    {
        # 去空
        $data=array_filter($data);
        #签名步骤一：按字典序排序参数
        ksort($data);
        #将数组转成url形式
        $string_a=http_build_query($data);
        $string_a=urldecode($string_a);
        #签名步骤二：在string后加入KEY
        $string_sign_temp=$string_a."&key=".$this->config['key'];
        #签名步骤三：MD5加密
        $sign = md5($string_sign_temp);
        # 签名步骤四：所有字符转为大写
        $result=strtoupper($sign);
        return $result;
    }

    /**
     * 将xml转为array
     * @param  string $xml xml字符串
     * @return array       转换得到的数组
     */
    public function toArray($xml){
        #禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }

    /**
     *
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return 产生的随机字符串
     */
    public function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )
        {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }

    /**
     * 输出xml字符
     * @throws WxPayException
     **/
    public function toXml($data)
    {
        if(!is_array($data) || count($data) <= 0)
        {
            throw new WxPayException("数组数据异常！");
        }
        $xml = "<xml>";
        foreach ($data as $key=>$val){
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * 验证
     * @return array 返回数组格式的notify数据
     */
    public function notify()
    {
        $ret=['data'=>"",'code'=>-1,'str'=>""];
        // 获取xml
        $xml=file_get_contents('php://input', 'r');
        # 转成php数组
        $data=$this->toArray($xml);
        # 保存原sign
        $data_sign=$data['sign'];
        # sign不参与签名
        unset($data['sign']);
        $sign=$this->makeSign($data);
        # 判断签名是否正确  判断支付状态
        if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS')
        {
            $result=$data;
        }else{
            $result=false;
        }

        # 返回状态给微信服务器
        if ($result)
        {
            $ret['str']='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            $ret['code']=1;
            $ret['data']=$result;
        }else{
            $ret['str']='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
        }
        return $ret;
    }
}